/*
 * HardDisk.cpp
 *
 *  Created on: Jul 11, 2009
 *      Author: avishai
 */

#include "HardDisk.h"


HardDisk::HardDisk(char* filename) {

	ifstream ifs(DISKFILE, ios::in | ios_base::binary);

	if (ifs) {
		cout << DISKFILE << " exist, read data" << endl;
		//ifs.seekg(0, ios::beg);
//		int location =0;
		cout<<"pos: "<<ifs.tellg()<<endl;
		ifs.read((char*) &kalEl.fileSystemSize, sizeof(int));
		cout <<"file system size = " << kalEl.fileSystemSize << endl;
		cout<<"pos: "<<ifs.tellg()<<endl;
		ifs.read((char*) &kalEl.blockSize, sizeof(int));
		cout << "block size = " << kalEl.blockSize << endl;
		cout<<"pos: "<<ifs.tellg()<<endl;
		ifs.read((char*) &kalEl.Inode_root, sizeof(int));
		cout << "root inode = " << kalEl.Inode_root << endl;
		cout<<"pos: "<<ifs.tellg()<<endl;
		ifs.read((char*)&kalEl.freeBlocksNum, sizeof(int));
		cout<<"number of free blocks : "<<kalEl.freeBlocksNum<<endl;
		kalEl.Freeblocks = ifs.tellg();
		cout<<"block of free blocks begin at "<<kalEl.Freeblocks<<endl;
		cout<<"pos: "<<ifs.tellg()<<endl;
		ifs.seekg(kalEl.blockSize, ios::cur);
		cout<<"pos: "<<ifs.tellg()<<endl;
		ifs.read((char*)&kalEl.nextFreeBlock, sizeof(int));
		cout<<"next free block = "<<kalEl.nextFreeBlock<<endl;
		cout<<"pos: "<<ifs.tellg()<<endl;
		ifs.read((char*)&kalEl.lastBlock, sizeof(int));
		cout<<"last free block : "<<kalEl.lastBlock<<endl;
		cout<<"pos: "<<ifs.tellg()<<endl;
		ifs.read((char*)&kalEl.numOfInodes, sizeof(int));
		cout<<"number of inodes : "<<kalEl.numOfInodes<<endl;
		cout<<"pos: "<<ifs.tellg()<<endl;
		ifs.read((char*)&kalEl.numOfFreeInodes, sizeof(int));
		cout<<"number of free Inodes : "<<kalEl.numOfFreeInodes<<endl;
		kalEl.FreeInodes = ifs.tellg();
		cout<<"pos: "<<ifs.tellg()<<endl;
		cout<<"block of free inodes : "<<kalEl.FreeInodes<<endl;
		ifs.seekg(kalEl.blockSize, ios::cur);
		cout<<"pos: "<<ifs.tellg()<<endl;
		ifs.read((char*)&kalEl.nextFreeInode, sizeof(int));
		cout<<"next free Inode : "<<kalEl.nextFreeInode<<endl;
		cout<<"pos: "<<ifs.tellg()<<endl;
		ifs.read((char*)&kalEl.lastInodeBlock, sizeof(int));
		cout<<"last inode block : "<<kalEl.lastInodeBlock<<endl;
		ifs.close();


	} else {
		cout << DISKFILE << " does not exit, create!" << endl;
		ofstream ofs(DISKFILE, ios::out | ios_base::binary);
		if (ofs.fail()) {
			cout << "fail to create file" << endl;
		}
		//read config file

		ifs.open(filename, ios::in);
		if (!ifs) {
			cout << "failed to open config file" << endl;
		} else {
			cout<<"loading config file"<<endl;
			string dump;
			ifs >> dump;
			ifs >> dump;
			ifs >> kalEl.blockSize;
			ifs >> dump;
			ifs >> dump;
			ifs >> kalEl.numOfInodes;
			kalEl.numOfFreeInodes = kalEl.numOfInodes;
			ifs >> dump;
			ifs >> dump;
			ifs >> kalEl.fileSystemSize;
			ifs.close();
			cout << kalEl.blockSize << " , " << kalEl.numOfInodes << " , "
					<< kalEl.fileSystemSize << endl;
			//file system size need to be in blocks
			kalEl.fileSystemSize = kalEl.fileSystemSize / kalEl.blockSize;
			//end of data from config file
			int location = 0;
			ofs.write((char*) &kalEl.fileSystemSize, sizeof(int));
				 cout.setf(ios::hex, ios::basefield);
			cout<<"location "<<location<<" = file sys size in blocks = "<<kalEl.fileSystemSize<<endl;
			location +=4;
			ofs.write((char*) &kalEl.blockSize, sizeof(int));
			cout<<"location "<<location<<" = block size = "<<kalEl.blockSize<<endl;
			location +=4;
			kalEl.Inode_root = 1;
			ofs.write((char*) &kalEl.Inode_root, sizeof(int));
			cout<<"location "<<location<<" inode root = "<<kalEl.Inode_root<<endl;
			location +=4;
			int inodeArrayLocation = 10*INT_SIZE + 2* kalEl .blockSize;
			cout<<"inode array location: "<<inodeArrayLocation<<endl;

			int blockArrayLocation = inodeArrayLocation + I_NODE_SIZE
					* kalEl.numOfInodes;
			cout<<"block array location : "<<blockArrayLocation<<endl;

			//number of NODES in the free blocks list

			kalEl.freeBlocksNum = (kalEl.fileSystemSize*kalEl.blockSize - blockArrayLocation)/ kalEl.blockSize;
			cout<<"at the first there are "<<kalEl.freeBlocksNum<<" free blocks"<<endl;


			int numberOfPointers = ((kalEl.blockSize / INT_SIZE)-1);


			cout<<"number of pointers in block : "<<numberOfPointers<<endl;

			int blocksInFreeBlockList = (kalEl.freeBlocksNum%numberOfPointers?kalEl.freeBlocksNum/numberOfPointers+1:kalEl.freeBlocksNum/numberOfPointers);
			cout<<"number of NODES in the free block list : "<<blocksInFreeBlockList<<endl;

			//number of NODES in the free inodes list:
//			double blocksInFreeNodesList_tmp =((double)kalEl.numOfInodes / numberOfPointers);

			int blocksInFreeInodesList = (kalEl.numOfInodes%numberOfPointers?kalEl.numOfInodes/numberOfPointers+1:kalEl.numOfInodes/numberOfPointers);

			cout<<"number of nodes in the free inodes list : "<<blocksInFreeInodesList<<endl;


			int zero = 0;

			//BLOCK CONTROL

			kalEl.freeBlocksNum = kalEl.freeBlocksNum -  (blocksInFreeBlockList-1) - (blocksInFreeInodesList-1) ;
			cout<<"num of free blocks : "<<kalEl.freeBlocksNum<<endl;
			ofs.write((char*) &kalEl.freeBlocksNum, sizeof(int));
			cout<<"location "<<location<<" = number of free blocks  = "<<kalEl.freeBlocksNum<<endl;
			location +=4;


			cout<<"writing block of free blocks"<<endl;
			kalEl.Freeblocks = location;
			cout<<"first pointer will be for: "<<blockArrayLocation<<endl;
			//write a block of free block pointers
			int count=0;
			for (int i = 0; i < numberOfPointers; i++) {
				int value = (blockArrayLocation + (kalEl.blockSize * i));
				cout<<"i = "<<i<<"("<<count<<") writing block num: "<<value<<" in location "<<location<<endl;
				ofs.write((char*) &value,sizeof(int));
				location +=4;
				count++;
			}
			blockArrayLocation+=(numberOfPointers)*kalEl.blockSize;

			//set the rest of the list...
			int saved_location = location;
			kalEl.lastBlock = 0;
			while (blocksInFreeBlockList > 1){
				//set the "next" pointer to the first free block
				cout<<"next pointer to : "<<blockArrayLocation<<"in location : "<<location<<endl;
				kalEl.lastBlock = blockArrayLocation;
				ofs.write((char*)&blockArrayLocation,sizeof(int));
				location+=4;
				ofs.seekp(blockArrayLocation,ios::beg);
				location = blockArrayLocation;
				blockArrayLocation+=kalEl.blockSize;
				cout<<"first pointer will point to "<<blockArrayLocation<<endl;
				int i=0;
				for (; i<numberOfPointers && count< kalEl.freeBlocksNum; i++){
					int value = (blockArrayLocation + (kalEl.blockSize*i));
					cout<<"i = "<<i<<"("<<count<<") writing block num: "<<value<<" in location "<<location<<endl;
					ofs.write((char*)&value, sizeof(int));
					location+=4;
					count++;
				}
				blockArrayLocation+=(i)*kalEl.blockSize;
				cout<<"new blockArrayLocation: "<<blockArrayLocation<<endl;
				blocksInFreeBlockList--;
			}
			//set the "next" pointer to 0, no more nodes
			ofs.write((char*) &zero, INT_SIZE);
			cout<<"\"next\" block pointer (0) is in : "<<location<<endl;
			location +=4;




			location = saved_location+4;
			ofs.seekp(location,ios::beg);
			//pointer to the first block
			kalEl.nextFreeBlock = 4*INT_SIZE;
			ofs.write((char*) &kalEl.nextFreeBlock, sizeof(int));
			cout<<"first free block offset is : "<<kalEl.nextFreeBlock<<" wrote in location : "<<location<<endl;
			location +=4;


			//pointer to the last block
			ofs.write((char*)&kalEl.lastBlock, INT_SIZE);
			cout<<"last block (offset) location  : "<<location<<endl;
			location +=4;
			cout<<"last Block (offset) pointer: "<<kalEl.lastBlock<<endl;




			//INODE control:
			ofs.write((char*) &kalEl.numOfInodes, INT_SIZE);
			cout<<"number of inodes: "<<kalEl.numOfInodes<<" location : "<<location<<endl;
			location +=4;
			//free inodes in the system:
			ofs.write((char*) &kalEl.numOfFreeInodes, INT_SIZE);
			cout<<"number of free inodes: "<<kalEl.numOfFreeInodes<<" location : "<<location<<endl;
			location +=4;

			kalEl.FreeInodes = location;

			//block of free inodes
			int limit = (kalEl.blockSize/INT_SIZE)-1;
			int nextInode = inodeArrayLocation;
			for (int i =0; i<limit ; i++){
				int value = nextInode;//(inodeArrayLocation + (i*I_NODE_SIZE));
				nextInode+=I_NODE_SIZE;
				ofs.write((char*) &value, sizeof(int));
				cout<<"inode pointer "<<i<<" -> "<<value<<" in location "<<location<<endl;
				location +=4;
			}
//			set the rest of the list
			saved_location = location;
			kalEl.lastInodeBlock = 0;
			while (blocksInFreeInodesList >1){
				//set the "next" pointer to the first free block
				cout<<"the next NODE will be in : "<<blockArrayLocation<<"location : "<<location<<endl;
				kalEl.lastInodeBlock = blockArrayLocation;
				ofs.write((char*)&blockArrayLocation, sizeof(int));
				ofs.seekp(blockArrayLocation,ios::beg);
				location=blockArrayLocation;
				for (int i=0; i<limit; i++){
					int value = nextInode;
					nextInode+=I_NODE_SIZE;
					ofs.write((char*)&value, sizeof(int));
					cout<<"inode pointer "<<i<<" -> "<<value<<" in location "<<location<<endl;
					location+=4;
				}
				blockArrayLocation+=kalEl.blockSize;
				blocksInFreeInodesList--;
			}
			//pointer to the next free inodes list
			ofs.write((char*)&zero, sizeof(int));
			location +=4;

			location = saved_location+4;
			ofs.seekp(location,ios::beg);
			kalEl.nextFreeInode = kalEl.FreeInodes;
			ofs.write((char*) &kalEl.nextFreeInode, sizeof (int));
			cout<<"the first inode location is: "<<kalEl.nextFreeInode<<" location = "<<location<<endl;
			location+=4;


			//pointer to the last free inode:
			cout<<"last inode : "<<kalEl.lastInodeBlock<<" location = "<<location<<endl;
			ofs.write((char*)&kalEl.lastInodeBlock, sizeof(int));
			location +=4;
			cout<<"write the inode array"<<endl;

			//inode array:
			for (int i = 0; i< kalEl.numOfInodes; i++){
				cout<<"reseting inode "<<i<<endl;
				int counter =0;
				cout<<" inode number "<<i<<" start location: "<<location<<endl;
				for (int j=0; j<I_NODE_SIZE; j++){
					ofs.write((char*)&zero, sizeof(char));
					location +=sizeof(char);
					counter+=sizeof(char);
				}
				cout<<" inode numebr "<<i<<" end location: "<<location<<endl;
				cout<<"counter = "<<counter<<endl;
			}

			int one = 1;


			cout<<"padding file, location = "<<location<<endl;
			for (int i=location; i<kalEl.fileSystemSize*kalEl.blockSize; i++){
				ofs.write((char*)&zero, sizeof(char));
				location +=1;
			}
			cout<<"end?"<<endl;
			ofs.close();
		}
	}
//	inodeListInit();
}

HardDisk::~HardDisk() {
	cout << "dtor" << endl;

}

///////////////////////////////////////////////////////////////////serving functions////
void HardDisk::inodeListInit(){
	for(int i=0;i<kalEl.numOfInodes;i++){
		inode ind;
		ind.fType=0;
		ind.fileSize=0;
		Block b;
		for(int i=0;i<10;i++){
			ind.firstTen[i]=b;
		}

		ind.restOfBlocks=NULL;
		iNodes.push_back(ind);
	}
}
int HardDisk::findNextFreeNode(){
	//check if still have inodes
	if (kalEl.numOfFreeInodes==0){
			return 0;
	}
	return kalEl.nextFreeInode;
}
void HardDisk::removeFromFreeNodeLst(int node_id){
	int inodeArrayStart=kalEl.blockSize*2+8*INT_SIZE;
	int freeInodeAdress=kalEl.FreeInodes;
	int inodeNum=(freeInodeAdress-inodeArrayStart)/50;
	if (inodeNum<15){//regular case
	//	clr_node_status(freeInodeAdress,inodeNum,I_NODE_SIZE);
		kalEl.nextFreeBlock+=4;//advance pointer to next free inode
	}else{//we need to copy the next block to the first and erase it.

	}



	kalEl.numOfFreeInodes--;


}
void HardDisk::clr_node_status(int freeInodeAdress,int blockSize){
	int zero = 0;
	ofstream ofs(DISKFILE, ios::out | ios_base::binary);
	ofs.seekp(freeInodeAdress,ios::beg);
	for (int j=0; j<blockSize; j++){
		ofs.write((char*)&zero, sizeof(char));
	}
}
/////////////////////////////////////////////////////////////serving functions end////


/*allocates a new i-node and returns its number	 */
int HardDisk::allocateInode(){
	/*
	int node_id=findNextFreeNode();
	if (node_id){
		removeFromFreeNodeLst(node_id);
		return node_id;
	}
	//no free inode left...
	return -1;
	*/
}

/*
 * frees the specified i-node
 * and also frees all the data blocks pointed to by it
 * (directly or indirectly).
 */
void HardDisk::freeInode(int i_node){
	ofstream ofs(DISKFILE, ios::out | ios_base::binary);
/*
	//phisical delete old inode:
	int indoesOffset=kalEl.FreeInodes;
	indoesOffset+=i_node*50;//point to our Inode
	ofs.seekp(indoesOffset,ios::beg);
	for (int j=0; j<I_NODE_SIZE; j++){
		ofs.write((char*)&zero, sizeof(char));
	}
	//add to free inode list:
	int nextFree=kalEl.lastInodeBlock??*/
}

/*allocates a data block and returns its number	 */
int HardDisk::allocateDataBlock(){
	int res=0;
	int blockArrayLocation = 10*INT_SIZE + 2*kalEl.blockSize+I_NODE_SIZE*kalEl.numOfInodes;
	int save =0;
//	cout<<"allocate data block"<<endl;
	if (kalEl.Freeblocks==0){
		perror("no more blocks to allocate");
		return -1;
	}
	ifstream fs (DISKFILE, ios::app | ios::binary);
	cout<<"kalEl.nextFreeBlock: "<<kalEl.nextFreeBlock<<endl;
	fs.seekg(kalEl.nextFreeBlock, ios::beg);
	cout<<"file is in offset : "<<fs.tellg()<<endl;
	fs.read((char*)&res, sizeof(int));
	cout<<"block offset is: "<<res<<endl;
	res= (res-blockArrayLocation)/INT_SIZE;

//	ifs.close();
//	ofstream ofs(DISKFILE, ios::app | ios::binary);
//	ofs.seekp(76, ios::beg);

	return res;
}

/*
 * frees the specified block.
 *Use (and update) the maps in the super
 *block in these allocate/free methods.
 */
void HardDisk::freeDataBlock(int dblock){
	/*
	int blockStart=kalEl.blockSize*2+8*INT_SIZE +50*kalEl.numOfInodes;
	int blockLocation=blockStart+kalEl.blockSize*dblock;
	int lastFreePointer=kalEl.lastBlock;
	int numOfPointersInBlock=kalEl.blockSize/INT_SIZE;
	cout<<"delete: block number "<<dblock<<endl;
	clr_node_status(blockLocation,kalEl.blockSize);

	//add block to free list
	bool foundSpace=true;
	for(int i=lastFreePointer;i<numOfPointersInBlock||foundSpace;i+=4){
		if (*((int*)lastFreePointer)==0){
			(*(int*)lastFreePointer)=blockLocation;
			foundSpace=false;
		}
	}
	//allocate new block of free block pointers:
	if (!foundSpace){

	}
*/
}

/*return the type of the file (normal/directory/soft-link)	 */
int HardDisk::getInodeType(int i_node){

}

/* set the type of the file (normal/directory/soft-link).	 */
void HardDisk::setInodeType(int i_node, int filetype){

}

/*
 *return the number of the i-th data block containing the data of this file
 *(for 0<=i<=9 this is just stored in the 10 pointers in this i_node, for i>9,
 * you must retrieve it from the indirect data block..).
 */
int HardDisk::getDataBlock(int i_node, int i){

}

/* sets the i-th data block of this file to be dblockNum.	 */
void HardDisk::setDataBlock(int i_node, int i, int dblockNum){

}

/*read the entire content of the dblockNum's and copy it into buf.	 */
void HardDisk::readBlock(int dblockNum, char* buf){

}

/*overwrite the content of block with number dblockNum with the newdata block.
 * Assume newdata's length is equal to dataBlockSize.
 */
void HardDisk::writeBlock(int dblockNum, char* newdata){

}

/*
 *return the file size saved in the given i_node.
 *This is the exact size of the file in bytes
 *not the number of blocks used by this file.
 */
int HardDisk::getFileSize(int i_node){

}

/* set the file size.	 */
void HardDisk::setFileSize(int i_node, int newSize){

}


